#include "BQ76920.H"

//报警回调接口
BQ_ConfigDataTypedef BQ_Config_Data;
//BQ寄存器结构体
static RegisterGroup Registers = {0};

static float Gain=0;//ADC增益
static int16_t iGain=0;;
static int8_t Adcoffset;//ADC偏移量

/*********************************温度*****************************/
static uint8_t indexTemp=6;//温度数组索引
static uint8_t TempSampleMode=0;//温度采样模式：0外部热敏电阻采样，1内部芯片采样
BQ_SampleDataTypedef BQ_SampleData={0};//传感器数据

// 10K NTC 5% 热敏电阻转换表
static const uint16_t TemD[31] =
{
	2890,2817,2735,2642,2541,2431,2318,2202,2127,2007,  
	1846,1687,1546,1422,1309,1202,1099,1001,907,817,
	727,677,613,550,496,452,414,379,345,311,277
};

/*热敏电阻温ADC转化为实际温度*/
//热敏电阻阻值随温度变化不是线性的，所以用公式法误差很大，采用线性插值算法，
//求距离该ADC值最近的两点温度近似拟合出线性公式，根据到这两个点的距离来分配它们的比重，计算出该点温度
//线性插值：y=y0+[(y1-y0)/(x1-x0)]*(x-x0)
//下面每段区间都用线性插值法，但是为了加快查找速度，划分了比较区间。
static int16_t TemperChange(uint16_t ADC_data)
{
    uint8_t index=0;
    uint8_t count=32;
    uint16_t Temp=0;
    index=indexTemp;//索引从6开始，正常温度大概处于这个值，加速查找，下一次查找从记录的索引开始
    if(ADC_data<TemD[index])//小于当前温度，查找距离最近的温度值
    {
        while(--count)
        {
            if(index<30)
            {
                index++;
                if(ADC_data>=TemD[index])
                {//
                    Temp=-200+index*(int)40;//为了避免浮点数运算，将温度放大10倍 ,表中的相邻阻值相差4度                                   
                    Temp=Temp-((int)40)*(ADC_data-TemD[index])/(TemD[index-1]-TemD[index]);
                    break;
                }
            }
            else
            {
                Temp=1000;//100度
                break;
            }
        }
    
    }
    else if(ADC_data>=TemD[index-1])//大于当前温度，查找距离最近的温度值
    {
        while(--count)
        {
            if(index>1)
            {
                index--;
                if(ADC_data<TemD[index-1])
                {
                    Temp=-200+index*(int)40;
                    Temp=Temp-((int)40)*(ADC_data-TemD[index])/(TemD[index-1]-TemD[index]);
                    break;
                }
            }
            else
            {
                Temp=-200;//-20度
                break;
            }
        }
    
    }
    else
    {
        Temp=-200+index*(int)40;
        Temp=Temp-((int)40)*(ADC_data-TemD[index])/(TemD[index-1]-TemD[index]);
                    
    }
    indexTemp=index;//记录索引
    
    //判断是否为负数
    if(Temp&0x8000)//0x8000就是1000 0000 0000 0000，最高位为1表示负数
    {
        Temp&=~0x8000;//清除符号位，得到绝对值
        Temp=0x8000-Temp;//负数的绝对值
        Temp=Temp*90/100;//对计算值进行补偿
        Temp&=0x7fff;//清除可能的符号位
        Temp|=0x8000;//标记为负数
    }//得到一个经过补偿计算的负数值
    else
    {
        Temp=Temp*90/100;
    }
    return Temp;
}
/***************************CRC校验*************************************/
// CRC8 校验算法，用于计算数据的 8 位循环冗余校验值，检测数据传输或存储中的错误，是利用除法及余数的原理来作错误侦测】
/*
CRC 算法的基本思想是将传输的数据当做一个位数很长的数。将这个数模二除以另一个数。得到的余数作为校验数据附加到原数据后面。
实际应用时，发送方和接收方按以下方式通信：

发送方和接收方在通信前，约定好一个预设整数作为除数。
发送方在发送前根据原始数据和约定好的除数进行模二除法运算生成余数（即CRC码），然后将其附加到原始数据后面一起发送给接收方。
接收方收到后将其模二除以约定好的除数，当且仅当余数为0时接收方认为没有差错。
实际应用时，发送方和接收方按以下方式通信：

发送方和接收方在通信前，约定好一个预设整数作为除数。
发送方在发送前根据原始数据和约定好的除数进行模二除法运算生成余数（即CRC码），然后将其附加到原始数据后面一起发送给接收方。
接收方收到后将其模二除以约定好的除数，当且仅当余数为0时接收方认为没有差错。

*/
static uint8_t CRC8(uint8_t *ptr, uint8_t len, uint8_t key)
{
	uint8_t i, crc=0;
	
	while (len-- != 0)
	{
		for (i = 0x80; i != 0; i /= 2)
		{
			if ((crc & 0x80) != 0)
			{
				crc *= 2;
				crc ^= key;
			}
			else
			{
				crc *= 2;
			}

			if ((*ptr & i) != 0)
			{
				crc ^= key;
			}
		}
		ptr++;
	}
	return(crc);
}

/*****************************  BQ ***************************/
static bool BQ769X0_WriteRegisterByte(uint8_t Register,uint8_t data)//BQ写入一字节,Register目标寄存器地址，data要写入的数据
{
    uint8_t databuffer[2]={Register,data};
    struct I2C_MessageTypeDef msg={0};
    
    msg.addr=BQ_I2C_ADDR;//不用CRC，数据包含了7位地址，不需要左移
    msg.flags=I2C_WR;
    msg.buf=databuffer;
    msg.tLen=2;
    
    if(I2C_TransferMessages(&i2c1,&msg,1)!=1)//如果传输失败
    {
        printf("Write Register Byte Fail");
        return false;
    }
    return true;
}

bool BQ769X0_WriteRegisterByteWithCRC(uint8_t Register,uint8_t data)//BQ写入一字节加CRC校验，Register目标寄存器地址，data要写入的数据
{
    uint8_t dataBuffer[4];
    struct I2C_MessageTypeDef msg={0};
    
    dataBuffer[0]=BQ_I2C_ADDR<<1;//左移一位表示写模式
    dataBuffer[1]=Register;
    dataBuffer[2]=data;
    dataBuffer[3]=CRC8(dataBuffer,3,CRC_KEY);
    
    msg.addr=BQ_I2C_ADDR;
    msg.flags=I2C_WR;
    msg.buf=dataBuffer+1;//注意msg.buf指向dataBuffer + 1，跳过了I2C地址字节，因为I2C硬件会自动处理地址字节
    msg.tLen=3;
    
    if(I2C_TransferMessages(&i2c1,&msg,1)!=1)
    {
        printf("Write Register Byte with CRC Fail");
        return false;
    }
    return true;
}

static bool BQ769X0_WriteRegisterWordWithCRC(uint8_t Register,uint16_t data)//传输2字节加CRC校验，Register目标寄存器地址，data要写入的数据
{
    uint8_t dataBuffer[6];
    struct I2C_MessageTypeDef msg={0};
    
    dataBuffer[0]=BQ_I2C_ADDR<<1;
    dataBuffer[1]=Register;
    dataBuffer[2]=LOWBYTE(data);
    dataBuffer[3]=CRC8(dataBuffer,3,CRC_KEY);
    dataBuffer[4]=HIGHBYTE(data);
    dataBuffer[5]=CRC8(dataBuffer+4,1,CRC_KEY);//+4是因为要计算的是高字节
    
    msg.addr=BQ_I2C_ADDR;
    msg.flags=I2C_WR;
    msg.buf=dataBuffer+1;//注意msg.buf指向dataBuffer + 1，跳过了I2C地址字节，因为I2C硬件会自动处理地址字节
    msg.tLen=5;
    
    if(I2C_TransferMessages(&i2c1,&msg,1)!=1)
    {
        printf("Write Register Word With CRC Fail");
        return false;
    }
    return true;
}

static bool BQ769X0_WriteBlockWithCRC(uint8_t startAddress, uint8_t *buffer, uint8_t length)
{
    uint8_t index;
    uint8_t *bufferCRC,*pointer;
    struct I2C_MessageTypeDef msg={0};
    
    bufferCRC=(uint8_t *)BQ_MALLOC(2*length+2);//如果开辟内存失败,每个数据字节需要1字节存储数据 + 1字节存储CRC,额外2字节用于I2C地址和起始寄存器地址
    if(NULL==bufferCRC)
    {
        printf("Malloc Fail");
        return false;
    }
    pointer=bufferCRC;
    *pointer++=BQ_I2C_ADDR<<1;
    *pointer++=startAddress;
    *pointer++=*buffer;// 第一个数据字节
    *pointer=CRC8(bufferCRC,3,CRC_KEY); // 计算前3字节的CRC
    
    for(index=1;index<length;index++)//处理剩余数据字节
    {
        pointer++;
        buffer++;
        *pointer=*buffer;// 当前数据字节
        *(pointer+1)=CRC8(pointer,1,CRC_KEY);// 当前字节的CRC
        pointer++;
    }
    msg.addr=BQ_I2C_ADDR;
    msg.flags=I2C_WR;
    msg.buf=bufferCRC+1;//开辟的内存缓冲区存储了所有数据
    msg.tLen=2*length+1;//length个数据字节 + length个CRC字节 + 1个寄存器地址字节
    
    if(I2C_TransferMessages(&i2c1,&msg,1)!=1)
    {
        printf("Write Register Block With CRC Fail");
    }
    BQ_FREE(bufferCRC);
    return true;
}

static bool BQ769X0_ReadRegisterByte(uint8_t Register, uint8_t *data)
{
    struct I2C_MessageTypeDef msg[2]={0};
    
    msg[0].addr=BQ_I2C_ADDR;//iic读取数据要先写进一个数据将从机设置为读模式
    msg[0].flags=I2C_WR;
    msg[0].buf=&Register;
    msg[0].tLen=1;
    
    msg[1].addr=BQ_I2C_ADDR;
    msg[1].flags=I2C_RD;
    msg[1].buf=data;
    msg[1].tLen=1;
    
    if(I2C_TransferMessages(&i2c1,msg,2)!=2)
    {
        printf("Read Register Byte Fail");
        return false;
    }
    return true;
}    

bool BQ769X0_ReadRegisterByteWithCRC(uint8_t Register, uint8_t *data)
{
    uint8_t readBuffer[2],crcInput[2],crcValue;
    struct I2C_MessageTypeDef msg[2]={0};
    
    msg[0].addr=BQ_I2C_ADDR;//iic读取数据要先写进一个数据将从机设置为读模式
    msg[0].flags=I2C_WR;
    msg[0].buf=&Register;
    msg[0].tLen=1;
    
    msg[1].addr=BQ_I2C_ADDR;
    msg[1].flags=I2C_RD;
    msg[1].buf=readBuffer;
    msg[1].tLen=2;
    
    if(I2C_TransferMessages(&i2c1,msg,2)!=2)
    {
        printf("Read Register Byte With CRC Fail");
        return false;
    }
    
    crcInput[0]=(BQ_I2C_ADDR<<1)+1;
    crcInput[1]=readBuffer[0];
    
    crcValue=CRC8(crcInput,2,CRC_KEY);
    if(crcValue!=readBuffer[1])
    {
        printf("Read Register Byte CRC Check Fail");
        return false;
    }
    
    *data=readBuffer[0];
    
    return true;
}

static bool BQ769X0_ReadRegisterWordWithCRC(uint8_t Register, uint16_t *data)
{
    uint8_t readBuffer[4],crcInput[2],crcValue;
    struct I2C_MessageTypeDef msg[2]={0};
    
    msg[0].addr=BQ_I2C_ADDR;//iic读取数据要先写进一个数据将从机设置为读模式
    msg[0].flags=I2C_WR;
    msg[0].buf=&Register;
    msg[0].tLen=1;
    
    msg[1].addr=BQ_I2C_ADDR;
    msg[1].flags=I2C_RD;
    msg[1].buf=readBuffer;
    msg[1].tLen=4;
    
     if(I2C_TransferMessages(&i2c1,msg,2)!=2)
    {
        printf("Read Register Word With CRC Fail");
        return false;
    }
    
    crcInput[0]=(BQ_I2C_ADDR<<1)+1;//添加iic读标志
    crcInput[1]=readBuffer[0];
    
    crcValue=CRC8(crcInput,2,CRC_KEY);
    if(crcValue!=readBuffer[1])
    {
        printf("Read Register Word CRC 1 Check Fail");
        return false;
    }
    
    crcValue=CRC8(readBuffer+2,1,CRC_KEY);
    if(crcValue!=readBuffer[3])
    {
        printf("Read Register Word CRC 2 Check Fail");
        return false;
    }
    *data=(readBuffer[2]<<8)|readBuffer[0];//将读取到的高8位和低8位合并为一字节
    
    return true;
}

static bool BQ769X0_ReadBlockWithCRC(uint8_t Register, uint8_t *buffer, uint8_t length)
{
    uint8_t index,crcValue,crcInput[2];
    uint8_t *readData,*startData;
    struct I2C_MessageTypeDef msg[2]={0};
    
    startData=(uint8_t *)BQ_MALLOC(2*length);
    if(NULL==startData)
    {
        printf("Read Register Block Malloc Fail");
        return false;
    }
    readData=startData;
    
    msg[0].addr=BQ_I2C_ADDR;//iic读取数据要先写进一个数据将从机设置为读模式
    msg[0].flags=I2C_WR;
    msg[0].buf=&Register;
    msg[0].tLen=1;
    
    msg[1].addr=BQ_I2C_ADDR;
    msg[1].flags=I2C_RD;
    msg[1].buf=readData;
    msg[1].tLen=length*2;
    
    if(I2C_TransferMessages(&i2c1,msg,2)!=2)
    {
        BQ_FREE(startData);
        printf("Read Register Block With CRC Cail");
        return false;
    }
    crcInput[0]=(BQ_I2C_ADDR<<1)+1;
    crcInput[1]=readData[0];
    
    crcValue=CRC8(crcInput,2,CRC_KEY);
    readData++;
    if(crcValue!=*readData)
    {
        BQ_FREE(startData);
        printf("Read Register Block CRC 1 Check Fail");
        return false;
    }
    else
    {
        *buffer=*(readData-1);
    }
    
    for(index=1;index<length;index++)
    {
       readData++;
       crcValue=CRC8(readData,1,CRC_KEY);
       readData++;
       buffer++;
       
        if(crcValue!=*readData)
        {
            BQ_FREE(startData);
            printf("Read Register Block CRC Check Fail");
            return false;
        }
        else
        {
            *buffer=*(readData-1);// -1来获取对应的数据字节，确保在校验CRC后能正确获取对应的数据字节
        }
    }
    BQ_FREE(startData);
    return true;
}

/***************************数据采集************************************/
/*热敏电阻温度采集*/
void BQ_Temper_Collect(void)
{
    uint16_t Temp=0;
    uint32_t result=0;
    uint8_t *pdata=NULL;
    
    if(TempSampleMode!=0)
    {
        TempSampleMode=0;//选择热敏电阻采集温度的方式
        if(BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1,0x18)!=true)
        {
            DBG_PRINTF("Update Tsx Temperature Fail");
        }
        BQ_delay(2000);//如果不是热敏电阻采集温度的方式，设置为2s此模式后采样
    }
    
    //读取2字节温度数据
    if(BQ769X0_ReadBlockWithCRC(TS1_HI_Byte,&(Registers.TS1.TS1Byte.TS1_HI),10)!=true)
    {
        DBG_PRINTF("Update Tsx Temperature Fail");
    }
  //  pdata=&Registers.TS1.TS1Byte.TS1_HI;
    for(int i=0;i<5;i++)//
    {
        Temp=(Registers.TS1.TS1Byte.TS1_HI<<8)|(Registers.TS1.TS1Byte.TS1_LO);//将温度数据合成一个16位数据
        result=(uint32_t)Temp*382/1000;//382源于芯片手册7.3.1.1.4，ADC LSB value 382 µV
        BQ_SampleData.TsxTemperatrue[i]=TemperChange(result)/10.0;//读取到的是电阻值，要经过ADC转换
        pdata+=2;
        printf("Temp=%d %.3f\r\n",i,BQ_SampleData.TsxTemperatrue[i]);
    }
   
}

/*单节电压数据采集，数据手册显示250ms更新一次*/
void BQ_singal_Volt(void)
{
    uint16_t data=0;
    uint8_t *singal_pata=NULL;
    uint32_t ltemp=0;
   // printf("!!!!!!!!!!!!!!!!!");
    /*从起始地址开始，连续读10字节*/
    if(BQ769X0_ReadBlockWithCRC(VC1_HI_Byte,&(Registers.VCell1.VCell1Byte.VC1_HI),10)!=true)//10==最大电池数<<1
    {
        DBG_PRINTF("Update Cell Voltage Fail");
    }
    singal_pata=&Registers.VCell1.VCell1Byte.VC1_HI;
    for(int i=0;i<5;i++)//轮询5节电池
    {
        data=(unsigned int)(*singal_pata<<8)+*(singal_pata+1);//左移8位，地址+1指向下一个地址的数据，两个数据组成一个16位数据
        BQ_SampleData.CellVoltage[i]=((unsigned long)data*iGain/1000+Adcoffset)/1000.0;//将采集到的数据通过*增益/放大倍数+偏移量/1000.0得到真实值，用放大倍数避免浮点数运算

//        ltemp=((unsigned long)data*iGain)/1000;
//        BQ_SampleData.CellVoltage[i]=(ltemp+Adcoffset)/1000.0;//将采集到的数据通过*增益/放大倍数+偏移量/1000.0得到真实值，用放大倍数避免浮点数运算
        singal_pata+=2;//指针+2，因为两个数据合成一个16位电压数值
        printf("BQ_singal_Volt %d=%.3f\r\n",i,BQ_SampleData.CellVoltage[i]);
        //OLED_ShowNum(2,2,BQ_SampleData.CellVoltage[1],4);
    }
}

/*电流采样，数据手册显示250ms更新一次*/
void BQ_Elec_Collet(void)
{
    int32_t elec;
    
    if(BQ769X0_ReadRegisterWordWithCRC(CC_HI_BYTE,&Registers.CC.CCWord)!=true)
    {
        DBG_PRINTF("Update Current Fail");
    }
    elec=Registers.CC.CCByte.CC_HI<<8|Registers.CC.CCByte.CC_LO;
    //转换为有符号数,因为充放电有正，负电流之分
    if(elec&0x8000)//如果为负数，符号位为1
    {
        elec=-((~elec+1)&0xffff);//取反，+1，取低16位，再取负号，得到有符号整数
    }
    BQ_SampleData.BatteryCurrent=((elec*8.44)/0.005)*0.000001;//0.005欧电流采样电阻，8.84uA分辨率，公式见7.3.1.1.3
    printf("Current = %.3f\r\n",BQ_SampleData.BatteryCurrent);
}

/*电压采集。250ms更新一次*/
//疑问：为什么要*4*Gain,解答：7.3.1.1.6
void BQ_Batvolt_Collet(void)
{
    uint16_t volt;
    if(BQ769X0_ReadRegisterWordWithCRC(BAT_HI_BYTE,&Registers.VBat.VBatWord)!=true)
    {
        DBG_PRINTF("Update Battery Voltage Fail");
    }
    volt=(((Registers.VBat.VBatByte.BAT_HI<<8)|Registers.VBat.VBatByte.BAT_LO)*4*Gain+5*Adcoffset);
    BQ_SampleData.BatteryVoltage=volt/1000.0;//单位是mv
    printf("VoltALL = %.3f\r\n",BQ_SampleData.BatteryVoltage);
}

/*获取增益和偏移量*/
void BQ_ADC_Gain(void)
{
    BQ769X0_ReadRegisterByteWithCRC(ADCGAIN1, &(Registers.ADCGain1.ADCGain1Byte));
	BQ769X0_ReadRegisterByteWithCRC(ADCGAIN2, &(Registers.ADCGain2.ADCGain2Byte));
	BQ769X0_ReadRegisterByteWithCRC(ADCOFFSET, &(Registers.ADCOffset));
    
    //Gain单位是 uv/LSB,OFFSET单位是mv
    Gain = (ADCGAIN_BASE + ((Registers.ADCGain1.ADCGain1Byte & 0x0C) << 1) + ((Registers.ADCGain2.ADCGain2Byte & 0xE0)>> 5)) / 1000.0;
	iGain = ADCGAIN_BASE + ((Registers.ADCGain1.ADCGain1Byte & 0x0C) << 1) + ((Registers.ADCGain2.ADCGain2Byte & 0xE0)>> 5);


	if (Registers.ADCOffset & 0x80)//P41
	{
		Adcoffset = 256 - (int16_t)Registers.ADCOffset * -1;
	}
	else
	{
		Adcoffset = Registers.ADCOffset;
	}
    
}

/*配置寄存器*/
static void BQ_Config(void)
{
    unsigned char ReadBuffer[8];
    //打开adc，选择外部NTC
    Registers.SysCtrl1.SysCtrl1Byte = 0x18;//P34
    
    //使能电流连续采样，关闭充放电mos
    Registers.SysCtrl2.SysCtrl2Byte = 0x40;
    
    //配置CC_CFG,必须为0x19
    Registers.CCCfg = 0x19;
    
    //写入配置到寄存器
    BQ769X0_WriteBlockWithCRC(SYS_CTRL1, &(Registers.SysCtrl1.SysCtrl1Byte), 8);
	BQ769X0_ReadBlockWithCRC(SYS_CTRL1, ReadBuffer, 8);
    
    //去掉buffer[0]最高位，防止因为接上负载检测置位而没通过校验
    if( (ReadBuffer[0]&0X7F) != Registers.SysCtrl1.SysCtrl1Byte
    || ReadBuffer[1] != Registers.SysCtrl2.SysCtrl2Byte
	|| ReadBuffer[2] != Registers.Protect1.Protect1Byte
	|| ReadBuffer[3] != Registers.Protect2.Protect2Byte
	|| ReadBuffer[4] != Registers.Protect3.Protect3Byte
	|| ReadBuffer[5] != Registers.OVTrip
	|| ReadBuffer[6] != Registers.UVTrip
	|| ReadBuffer[7] != Registers.CCCfg)
	{
		DBG_PRINTF("BQ769X0 config register fail,Please reset BMS board");

		while(1);
	}
    printf("jicunqi ok\r\n");
}

//检查是否接入负载，只有在没充电且CHG大于0.7才会检测到负载
bool BQ_Check_Load(void)
{
    BQ769X0_ReadRegisterWordWithCRC(SYS_CTRL1, (uint16_t *)&Registers.SysCtrl1.SysCtrl1Byte);
    if (Registers.SysCtrl2.SysCtrl2Bit.CHG_ON == 0) //不在充电状态
	{
		if (Registers.SysCtrl1.SysCtrl1Bit.LOAD_PRESENT)//判断PACK-是否是高电压，LOAD_PRESENT是负载存在标志
		{
			return true;
		}
	}
	return false;
}

//芯片唤醒引脚配置
static void BQ_TS1_SetOutMode(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    
    GPIO_InitStruct.Pin=BQ76920_TS1_Pin;
    GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull=GPIO_NOPULL;
    GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(BQ76920_TS1_GPIO_Port,&GPIO_InitStruct);
}
static void BQ_TS1_SetInMode(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    
    GPIO_InitStruct.Pin=BQ76920_TS1_Pin;
    GPIO_InitStruct.Mode=GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull=GPIO_NOPULL;
    GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(BQ76920_TS1_GPIO_Port,&GPIO_InitStruct);
}

/*唤醒*/
void BQ_Wakeup(void)
{
    BQ_TS1_SetOutMode();
    HAL_GPIO_WritePin(BQ76920_TS1_GPIO_Port, BQ76920_TS1_Pin, GPIO_PIN_SET);
    BQ_delay(1000);
    HAL_GPIO_WritePin(BQ76920_TS1_GPIO_Port, BQ76920_TS1_Pin, GPIO_PIN_RESET);
    BQ_TS1_SetInMode();//唤醒后设置为输入模式，避免影响温度采样
    BQ_delay(1000);
}

/*低功耗*/
void BQ_EntryShip(void)
{//P35表7-7
    BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x00);
	BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x01);
	BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL1, 0x02);
}

//控制充放电
void BQ_ControlDSGOrCHG(BQ_ControlTypedef ControlType,BQ_StateTypedef NewState)
{
    if(NewState==BQ_STATE_ENABLE)
    {
    Registers.SysCtrl2.SysCtrl2Byte|=ControlType;
    }
    else{
     Registers.SysCtrl2.SysCtrl2Byte&=~ControlType;
    }
   BQ769X0_WriteRegisterByteWithCRC(SYS_CTRL2,Registers.SysCtrl2.SysCtrl2Byte);
}

/*设置单个电芯均衡状态，就是往电池均衡功能控制寄存器CELLBAL10x01）写0/1*/
void BQ_CellBalanceControl(BQ_CellIndexTypedef CellIndex,BQ_StateTypedef NewState)
{
    static uint8_t Cell_BAL_value[3]={0};
    
    if(NewState==BQ_STATE_ENABLE)
    {
    Cell_BAL_value[0]|=CellIndex&0x1f;//控制1-5节电池
//    Cell_BAL_value[1]|=(CellIndex>>5)&0x1f;//控制6-10节电池
//    Cell_BAL_value[2]|=(CellIndex>>10)&0x1f;//控制11-15节电池
    }
    else if(NewState==BQ_STATE_DISABLE)
    {
    Cell_BAL_value[0]&=~(CellIndex&0x1f);
//    Cell_BAL_value[1]&=~((CellIndex>>5)&0x1f);
//    Cell_BAL_value[2]&=~((CellIndex>>10)&0x1f);
    }
    BQ769X0_WriteBlockWithCRC(CELLBAL1, Cell_BAL_value, 3);
}


/***********************************设置参数****************************************************/
/*设置短路放电保护（SCD）延时时间，就是往0x06写值*/
void BQ769X0_SCDDelaySet(BQ_SCDDelayTypedef SCDDelay)
{
    Registers.Protect1.Protect1Bit.SCD_DELAY=SCDDelay;
    BQ769X0_WriteRegisterByteWithCRC(PROTECT1,Registers.Protect1.Protect1Bit.SCD_DELAY);
}
/*设置过流放电保护（OCD）延时时间，就是往0x07写值*/
void BQ769X0_OCDDelaySet(BQ_OCDDelayTypedef OCDDelay)
{
    Registers.Protect2.Protect2Bit.OCD_DELAY=OCDDelay;
    BQ769X0_WriteRegisterByteWithCRC(PROTECT2,Registers.Protect2.Protect2Bit.OCD_DELAY);
}

/*设置欠压保护（UV）延时时间,就是往0x08写值*/
void BQ769X0_UVDelaySet(BQ_UVDelayTypedef UVDelay)
{
    Registers.Protect3.Protect3Bit.UV_DELAY=UVDelay;
    BQ769X0_WriteRegisterByteWithCRC(PROTECT3,Registers.Protect3.Protect3Bit.UV_DELAY);
}

/*设置过压保护（OV）延时时间,就是往0x08写值*/
void BQ769X0_OVDelaySet(BQ_OVDelayTypedef OVDelay)
{
    Registers.Protect3.Protect3Bit.OV_DELAY=OVDelay;
    BQ769X0_WriteRegisterByteWithCRC(PROTECT3,Registers.Protect3.Protect3Bit.OV_DELAY);
}

/*设置欠压保护阈值， 设置的值经过校准后写入欠压保护阈值寄存器，校准见7.3.1.2.1*/
void BQ769X0_UVPThresholdSet(uint16_t UVPThreshold)
{
    Registers.UVTrip = (uint8_t)((((uint16_t)((UVPThreshold - Adcoffset)/Gain/* + 0.5*/) - UV_THRESH_BASE) >> 4) & 0xFF);
    BQ769X0_WriteRegisterByteWithCRC(UV_TRIP, Registers.UVTrip);
}

/*设置过压保护（OV）阈值,表7-13*/
void BQ769X0_OVPThresholdSet(uint16_t OVPThreshold)
{
    Registers.OVTrip = (uint8_t)((((uint16_t)((OVPThreshold - Adcoffset)/Gain/* + 0.5*/) - OV_THRESH_BASE) >> 4) & 0xFF);
	BQ769X0_WriteRegisterByteWithCRC(OV_TRIP, Registers.OVTrip);
}


/*芯片初始化*/
void BQ_Init(void)
{
    //进入休眠再唤醒相当于复位
    BQ_EntryShip();
    BQ_delay(250);//P21
    BQ_Wakeup();
    
    //获取增益和偏移量
   BQ_ADC_Gain();
    
    //配置寄存器
    Registers.Protect1.Protect1Bit.SCD_THRESH = SCD_THRESH_89mV_44mV;
	Registers.Protect2.Protect2Bit.OCD_THRESH = OCD_THRESH_22mV_11mV;
	Registers.Protect1.Protect1Bit.SCD_DELAY  = BQ_Config_Data.SCDDelay;	
	Registers.Protect2.Protect2Bit.OCD_DELAY  = BQ_Config_Data.OCDDelay;	
	Registers.Protect3.Protect3Bit.UV_DELAY   = BQ_Config_Data.UVDelay;	
	Registers.Protect3.Protect3Bit.OV_DELAY   = BQ_Config_Data.OVDelay;
    
    //设置欠压，过压保护阈值
    //为什么要*1000，因为获取增益单位是mv
    Registers.OVTrip = (uint8_t)((((uint16_t)((TLB_OV_RELIEVE*1000 - Adcoffset)/Gain/* + 0.5*/) - OV_THRESH_BASE) >> 4) & 0xFF);
    Registers.UVTrip = (uint8_t)((((uint16_t)((TLB_UV_PROTECT*1000 - Adcoffset)/Gain/* + 0.5*/) - UV_THRESH_BASE) >> 4) & 0xFF);
    BQ_Config();
    printf("BQ769X0 Init successful!\r\n");
}

void BMS_DataInit(void)
{
    BQ_Config_Data.SCDDelay = BQ_SCD_DELAY_100us;
	BQ_Config_Data.OCDDelay = BQ_OCD_DELAY_320ms;
	BQ_Config_Data.UVDelay  = BQ_UV_DELAY_4s; 
	BQ_Config_Data.OVDelay  = BQ_OV_DELAY_2s;
}
